/* Copyright (c) [2025] [zhi1208@126.com]
 * SPDX-License-Identifier: MIT
 */

/*********************
 *      INCLUDES
 *********************/
#include "hmi_pg_def.h"
#include "hmi_tinyttf.h"
#include "hmi_png.h"
#include "hmi_subjects.h"
#include "hmi_config.h"
#include "hmi_pg_navigation.h"
#include "hmi_anim.h"
#include "hmi_bg.h"
#include "hmi_pages.h"

/*********************
 *      DEFINES
 *********************/

/**********************
 *      TYPEDEFS
 **********************/
typedef struct
{
    lv_obj_t *obj;
    lv_style_t *def;
    lv_style_t *active;
    uint32_t act_index;
    uint32_t max_index;
} page_indicator_t;

typedef struct
{
    int32_t target_width;
    int32_t target_height;
} function_btn_user_data_t;

/**********************
 *  STATIC PROTOTYPES
 **********************/
static void create();
static void del();
static void enter_anim(lv_anim_completed_cb_t cb, uint32_t delay);
static uint32_t exit_anim(lv_anim_completed_cb_t cb, hmi_pg_t *enter_pg);

static void top_bar_create();
static lv_obj_t *top_bar_icon_create(lv_obj_t *parent);
static void wifi_icon_ev_cb(lv_event_t *e);
#if (LVGL_VERSION_MAJOR >= 9 && LVGL_VERSION_MINOR >= 4 && LVGL_VERSION_PATCH >= 0)
static void lang_icon_ev_cb(lv_event_t *e);
#endif
static void time_click_ev_cb(lv_event_t *e);
static void wifi_subj_observer_cb(lv_observer_t *observer, lv_subject_t *subject);
static void bluetooth_subj_observer_cb(lv_observer_t *observer, lv_subject_t *subject);
#if (LVGL_VERSION_MAJOR >= 9 && LVGL_VERSION_MINOR >= 4 && LVGL_VERSION_PATCH >= 0)
static void translations_subj_observer_cb(lv_observer_t *observer, lv_subject_t *subject);
#endif
static void time_subj_observer_cb(lv_observer_t *observer, lv_subject_t *subject);

static void weather_box_create();
static lv_obj_t *weather_box_info_create(lv_obj_t *parent, const char *title, lv_observer_cb_t cb);
static void temperature_ob_cb(lv_observer_t *observer, lv_subject_t *subject);
static void weather_icon_ob_cb(lv_observer_t *observer, lv_subject_t *subject);
static void weather_ob_cb(lv_observer_t *observer, lv_subject_t *subject);
static void city_ob_cb(lv_observer_t *observer, lv_subject_t *subject);
static void humidity_ob_cb(lv_observer_t *observer, lv_subject_t *subject);
static void precip_ob_cb(lv_observer_t *observer, lv_subject_t *subject);
static void wind_scale_ob_cb(lv_observer_t *observer, lv_subject_t *subject);

static void function_box_create();
static lv_obj_t *function_box_page_create(lv_obj_t *parent);
static lv_obj_t *function_btn_create(lv_obj_t *parent, const char *icon_path, const char *title, uint32_t bg_color, lv_event_cb_t ev_cb);
static void function_box_scoll_ev_cb(lv_event_t *e);
static void function_btn_generic_ev_cb(lv_event_t *e);

static void led_strip_btn_click_cb(lv_event_t *e);
static void setting_btn_click_cb(lv_event_t *e);

static void page_indicator_create();
static void page_indicator_update();

/**********************
 *  STATIC VARIABLES
 **********************/
static hmi_pg_t home = {
    .name = "主页",
    .bg_color = HMI_BLUE,
    .create = create,
    .del = del,
    .enter_anim = enter_anim,
    .exit_anim = exit_anim};

static lv_font_t *time_font;
static lv_font_t *function_btn_title_font;
static lv_font_t *temperature_font;
static lv_font_t *temperature_unit_font;
static lv_font_t *weather_info_font;
static lv_font_t *weather_info_title_font;

static page_indicator_t page_indicator;

/**********************
 *      MACROS
 **********************/

/**********************
 *   GLOBAL FUNCTIONS
 **********************/
hmi_pg_t *hmi_pg_home()
{
    return &home;
}

/**********************
 *   STATIC FUNCTIONS
 **********************/
static void create()
{
    home.bg_y = HMI_BG_NORMAL;

    home.root = lv_obj_create(NULL);
    lv_obj_remove_style_all(home.root);
    lv_obj_set_flex_flow(home.root, LV_FLEX_FLOW_COLUMN);
    lv_obj_set_style_pad_row(home.root, 5, 0);
    lv_obj_set_flex_align(home.root, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
    lv_obj_remove_flag(home.root, LV_OBJ_FLAG_SCROLLABLE);

    top_bar_create();
    weather_box_create();
    function_box_create();
}

static void del()
{
    lv_obj_delete_async(home.root);
    home.root = NULL;

    if (time_font)
    {
        hmi_tiny_ttf_del(time_font);
        time_font = NULL;
    }

    if (function_btn_title_font)
    {
        hmi_tiny_ttf_del(function_btn_title_font);
        function_btn_title_font = NULL;
    }

    if (temperature_font)
    {
        hmi_tiny_ttf_del(temperature_font);
        temperature_font = NULL;
    }

    if (temperature_unit_font)
    {
        hmi_tiny_ttf_del(temperature_unit_font);
        temperature_unit_font = NULL;
    }

    if (weather_info_font)
    {
        hmi_tiny_ttf_del(weather_info_font);
        weather_info_font = NULL;
    }

    if (weather_info_title_font)
    {
        hmi_tiny_ttf_del(weather_info_title_font);
        weather_info_title_font = NULL;
    }
}

static void enter_anim(lv_anim_completed_cb_t cb, uint32_t delay)
{
    uint32_t child_cnt = lv_obj_get_child_count(home.root);

    lv_anim_t a;
    lv_anim_init(&a);
    lv_anim_set_duration(&a, HMI_ANIM_TIME);
    lv_anim_set_exec_cb(&a, hmi_anim_translate_y_exec_cb);
    lv_anim_set_values(&a, -HMI_ANIM_Y, 0);
    lv_anim_set_path_cb(&a, lv_anim_path_ease_in);

    for (uint32_t i = 0; i < child_cnt; ++i)
    {
        lv_obj_t *child = lv_obj_get_child(home.root, i);
        lv_anim_set_var(&a, child);
        lv_anim_set_delay(&a, delay + HMI_ANIM_DELAY * (i + 1));

        if (i == child_cnt - 1)
            lv_anim_set_completed_cb(&a, cb);

        lv_anim_start(&a);
        lv_obj_fade_in(child, HMI_ANIM_DELAY, delay + HMI_ANIM_DELAY * (i + 1));

        if (i == 2)
        {
            uint32_t cnt = lv_obj_get_child_count(child);
            for (uint32_t j = 0; j < cnt; ++j)
                lv_obj_fade_in(lv_obj_get_child(child, j), HMI_ANIM_TIME * 2, HMI_ANIM_DELAY * child_cnt + HMI_ANIM_TIME);
        }
    }
}
static uint32_t exit_anim(lv_anim_completed_cb_t cb, hmi_pg_t *enter_pg)
{
    return hmi_exit_anim_default(home.root, cb, enter_pg);
}

static void top_bar_create()
{
    lv_obj_t *top_bar = lv_obj_create(home.root);
    lv_obj_remove_style_all(top_bar);
    lv_obj_set_size(top_bar, lv_pct(100), lv_pct(15));
    lv_obj_set_style_pad_all(top_bar, 10, 0);
    lv_obj_set_style_pad_right(top_bar, 15, 0);

    static int32_t top_bar_col_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_FR(4), LV_GRID_TEMPLATE_LAST};
    static int32_t top_bar_row_dsc[] = {LV_GRID_CONTENT, LV_GRID_TEMPLATE_LAST};
    lv_obj_set_grid_dsc_array(top_bar, top_bar_col_dsc, top_bar_row_dsc);
    lv_obj_set_grid_align(top_bar, LV_GRID_ALIGN_SPACE_EVENLY, LV_GRID_ALIGN_SPACE_EVENLY);

    lv_obj_t *wifi_icon = top_bar_icon_create(top_bar);
    lv_obj_add_event_cb(wifi_icon, wifi_icon_ev_cb, LV_EVENT_CLICKED, NULL);

    lv_obj_t *bluetooth_icon = top_bar_icon_create(top_bar);
#if (LVGL_VERSION_MAJOR >= 9 && LVGL_VERSION_MINOR >= 4 && LVGL_VERSION_PATCH >= 0)
    lv_obj_t *lang_icon = top_bar_icon_create(top_bar);
#endif

    lv_obj_t *time = lv_label_create(top_bar);
    lv_obj_add_flag(time, LV_OBJ_FLAG_CLICKABLE);
    lv_obj_set_style_text_align(time, LV_TEXT_ALIGN_RIGHT, 0);
    if (!time_font)
        time_font = hmi_tiny_ttf_create_static("def_font", 15, 0);
    lv_obj_set_style_text_font(time, time_font, 0);
    lv_obj_set_style_text_color(time, lv_color_white(), 0);
    lv_obj_set_style_text_color(time, lv_palette_main(LV_PALETTE_GREY), LV_STATE_PRESSED);
    lv_obj_add_event_cb(time, time_click_ev_cb, LV_EVENT_CLICKED, NULL);

    lv_obj_set_grid_cell(wifi_icon, LV_GRID_ALIGN_CENTER, 0, 1, LV_GRID_ALIGN_CENTER, 0, 1);
    lv_obj_set_grid_cell(bluetooth_icon, LV_GRID_ALIGN_CENTER, 1, 1, LV_GRID_ALIGN_CENTER, 0, 1);
#if (LVGL_VERSION_MAJOR >= 9 && LVGL_VERSION_MINOR >= 4 && LVGL_VERSION_PATCH >= 0)
    lv_obj_set_grid_cell(lang_icon, LV_GRID_ALIGN_CENTER, 2, 1, LV_GRID_ALIGN_CENTER, 0, 1);
#endif
    lv_obj_set_grid_cell(time, LV_GRID_ALIGN_END, 4, 1, LV_GRID_ALIGN_CENTER, 0, 1);

#if (LVGL_VERSION_MAJOR >= 9 && LVGL_VERSION_MINOR >= 4 && LVGL_VERSION_PATCH >= 0)
    lv_obj_add_event_cb(lang_icon, lang_icon_ev_cb, LV_EVENT_CLICKED, NULL);
#endif

    lv_subject_add_observer_obj(&hmi_wifi_subj, wifi_subj_observer_cb, wifi_icon, NULL);
    lv_subject_add_observer_obj(&hmi_bluetooth_subj, bluetooth_subj_observer_cb, bluetooth_icon, NULL);
#if (LVGL_VERSION_MAJOR >= 9 && LVGL_VERSION_MINOR >= 4 && LVGL_VERSION_PATCH >= 0)
    lv_subject_add_observer_obj(&hmi_translations_subj, translations_subj_observer_cb, lang_icon, NULL);
#endif
    lv_subject_add_observer_obj(&hmi_dt_subj_group, time_subj_observer_cb, time, NULL);
}

static lv_obj_t *top_bar_icon_create(lv_obj_t *parent)
{
    lv_obj_t *icon = lv_image_create(parent);
    lv_obj_set_style_image_recolor_opa(icon, LV_OPA_COVER, LV_STATE_PRESSED);
    lv_obj_set_style_image_recolor(icon, lv_palette_main(LV_PALETTE_GREY), LV_STATE_PRESSED);
    lv_obj_add_flag(icon, LV_OBJ_FLAG_CLICKABLE);

    return icon;
}

void wifi_icon_ev_cb(lv_event_t *e)
{
    LV_UNUSED(e);
    hmi_pg_push(hmi_pg_wifi());
}

#if (LVGL_VERSION_MAJOR >= 9 && LVGL_VERSION_MINOR >= 4 && LVGL_VERSION_PATCH >= 0)
static void lang_icon_ev_cb(lv_event_t *e)
{
    lv_event_code_t code = lv_event_get_code(e);

    if (code == LV_EVENT_CLICKED)
    {
#if (LVGL_VERSION_MAJOR >= 9 && LVGL_VERSION_MINOR >= 4 && LVGL_VERSION_PATCH >= 0)
        lv_subject_set_int(&hmi_translations_subj, !lv_subject_get_int(&hmi_translations_subj));
#endif

        hmi_rebuild_all_pg();
    }
}
#endif

static void time_click_ev_cb(lv_event_t *e)
{
    LV_UNUSED(e);
    hmi_pg_push(hmi_pg_dt());
}

static void wifi_subj_observer_cb(lv_observer_t *observer, lv_subject_t *subject)
{
    bool state = lv_subject_get_int(subject);
    lv_obj_t *wifi_icon = lv_observer_get_target_obj(observer);

    if (state)
        lv_image_set_src(wifi_icon, hmi_png("wifi"));
    else
        lv_image_set_src(wifi_icon, hmi_png("wifi_slash"));
}

static void bluetooth_subj_observer_cb(lv_observer_t *observer, lv_subject_t *subject)
{
    bool state = lv_subject_get_int(subject);
    lv_obj_t *bluetooth_icon = lv_observer_get_target_obj(observer);

    if (state)
        lv_image_set_src(bluetooth_icon, hmi_png("bluetooth"));
    else
        lv_image_set_src(bluetooth_icon, hmi_png("bluetooth_slash"));
}

#if (LVGL_VERSION_MAJOR >= 9 && LVGL_VERSION_MINOR >= 4 && LVGL_VERSION_PATCH >= 0)
static void translations_subj_observer_cb(lv_observer_t *observer, lv_subject_t *subject)
{
    int32_t lang = lv_subject_get_int(subject);
    lv_obj_t *lang_icon = lv_observer_get_target_obj(observer);

    if (lang == 0)
        lv_image_set_src(lang_icon, hmi_png("translate_c2e"));
    else if (lang == 1)
        lv_image_set_src(lang_icon, hmi_png("translate_e2c"));
}
#endif

static void time_subj_observer_cb(lv_observer_t *observer, lv_subject_t *subject)
{
    lv_obj_t *time = lv_observer_get_target_obj(observer);

    lv_subject_t *year_subj = lv_subject_get_group_element(subject, 0);
    lv_subject_t *month_subj = lv_subject_get_group_element(subject, 1);
    lv_subject_t *day_subj = lv_subject_get_group_element(subject, 2);
    lv_subject_t *weekday_subj = lv_subject_get_group_element(subject, 3);
    lv_subject_t *hour_subj = lv_subject_get_group_element(subject, 4);
    lv_subject_t *minute_subj = lv_subject_get_group_element(subject, 5);

    int32_t year = lv_subject_get_int(year_subj);
    int32_t month = lv_subject_get_int(month_subj);
    int32_t day = lv_subject_get_int(day_subj);
    int32_t weekday = lv_subject_get_int(weekday_subj);
    int32_t hour = lv_subject_get_int(hour_subj);
    int32_t minute = lv_subject_get_int(minute_subj);

#if (LVGL_VERSION_MAJOR >= 9 && LVGL_VERSION_MINOR >= 4 && LVGL_VERSION_PATCH >= 0)
    const char *weekday_str[] = {lv_tr("星期日"), lv_tr("星期一"), lv_tr("星期二"), lv_tr("星期三"), lv_tr("星期四"), lv_tr("星期五"), lv_tr("星期六")};
#else
    const char *weekday_str[] = {"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};
#endif
    lv_label_set_text_fmt(time, "%02ld:%02ld\n%ld-%ld-%ld %s", hour, minute, year, month, day, weekday_str[weekday]);
}

static void weather_box_create()
{
    lv_obj_t *weather_box = lv_obj_create(home.root);
    lv_obj_set_flex_grow(weather_box, 4);
    lv_obj_set_width(weather_box, lv_pct(94));
    lv_obj_set_style_border_width(weather_box, 0, 0);
    lv_obj_set_style_bg_color(weather_box, lv_color_hex(0xa1eafb), LV_STATE_USER_1);
    lv_obj_set_style_bg_color(weather_box, lv_color_hex(0xfbe1b6), LV_STATE_USER_2);
    lv_obj_set_style_bg_color(weather_box, lv_color_hex(0xffdfdf), LV_STATE_USER_3);

    static int32_t weather_box_col_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST};
    static int32_t weather_box_row_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST};
    lv_obj_set_grid_dsc_array(weather_box, weather_box_col_dsc, weather_box_row_dsc);
    lv_obj_set_grid_align(weather_box, LV_GRID_ALIGN_SPACE_EVENLY, LV_GRID_ALIGN_SPACE_EVENLY);

    if (!temperature_font)
        temperature_font = hmi_tiny_ttf_create_static("def_font", 60, 0);

    if (!temperature_unit_font)
        temperature_unit_font = hmi_tiny_ttf_create_static("def_font", 18, 0);

    if (!weather_info_font)
        weather_info_font = hmi_tiny_ttf_create_static("def_font", 18, 0);

    if (!weather_info_title_font)
        weather_info_title_font = hmi_tiny_ttf_create_static("def_font", 10, 0);

    lv_obj_t *icon = lv_image_create(weather_box);
    lv_image_set_scale(icon, 220);
    lv_subject_add_observer_obj(&hmi_weather_subj_group, weather_icon_ob_cb, icon, NULL);

    lv_obj_t *temperature = lv_obj_create(weather_box);
    lv_obj_remove_style_all(temperature);
    lv_obj_set_size(temperature, LV_SIZE_CONTENT, LV_SIZE_CONTENT);

    lv_obj_t *temp_val = lv_label_create(temperature);
    lv_obj_set_style_text_font(temp_val, temperature_font, 0);
    lv_obj_set_style_text_color(temp_val, lv_color_black(), 0);
    lv_subject_add_observer_obj(&hmi_weather_subj_group, temperature_ob_cb, temp_val, weather_box);

    lv_obj_t *cont = lv_obj_create(weather_box);
    lv_obj_remove_style_all(cont);
    lv_obj_set_size(cont, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
    lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_COLUMN);
    lv_obj_set_flex_align(cont, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START);
    lv_obj_set_style_pad_hor(cont, 10, 0);
    lv_obj_set_style_border_side(cont, LV_BORDER_SIDE_LEFT, 0);
    lv_obj_set_style_border_width(cont, 2, 0);
    lv_obj_set_style_border_opa(cont, LV_OPA_20, 0);
    lv_obj_set_style_border_color(cont, lv_palette_main(LV_PALETTE_GREY), 0);

    lv_obj_t *weather = lv_label_create(cont);
    lv_label_set_long_mode(weather, LV_LABEL_LONG_MODE_SCROLL);
    lv_obj_set_style_text_font(weather, weather_info_font, 0);
    lv_obj_set_style_text_color(weather, lv_color_black(), 0);
    lv_subject_add_observer_obj(&hmi_weather_subj_group, weather_ob_cb, weather, NULL);

    lv_obj_t *city = lv_label_create(cont);
    lv_obj_set_style_text_font(city, weather_info_title_font, 0);
    lv_obj_set_style_text_color(city, lv_palette_main(LV_PALETTE_GREY), 0);
    lv_subject_add_observer_obj(&hmi_weather_subj_group, city_ob_cb, city, NULL);

    lv_obj_t *humidity = weather_box_info_create(weather_box, "相对湿度", humidity_ob_cb);
    lv_obj_t *precip = weather_box_info_create(weather_box, "降水概率", precip_ob_cb);
    lv_obj_t *wind_scale = weather_box_info_create(weather_box, "风力等级", wind_scale_ob_cb);

    lv_obj_set_grid_cell(icon, LV_GRID_ALIGN_START, 0, 2, LV_GRID_ALIGN_CENTER, 0, 1);
    lv_obj_set_grid_cell(temperature, LV_GRID_ALIGN_END, 2, 2, LV_GRID_ALIGN_END, 0, 1);
    lv_obj_set_grid_cell(cont, LV_GRID_ALIGN_CENTER, 0, 1, LV_GRID_ALIGN_END, 1, 1);
    lv_obj_set_grid_cell(humidity, LV_GRID_ALIGN_CENTER, 1, 1, LV_GRID_ALIGN_END, 1, 1);
    lv_obj_set_grid_cell(precip, LV_GRID_ALIGN_CENTER, 2, 1, LV_GRID_ALIGN_END, 1, 1);
    lv_obj_set_grid_cell(wind_scale, LV_GRID_ALIGN_CENTER, 3, 1, LV_GRID_ALIGN_END, 1, 1);
}

static lv_obj_t *weather_box_info_create(lv_obj_t *parent, const char *title, lv_observer_cb_t cb)
{
    lv_obj_t *cont = lv_obj_create(parent);
    lv_obj_remove_style_all(cont);
    lv_obj_set_size(cont, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
    lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_COLUMN);
    lv_obj_set_flex_align(cont, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START);
    lv_obj_set_style_pad_hor(cont, 10, 0);
    lv_obj_set_style_border_side(cont, LV_BORDER_SIDE_LEFT, 0);
    lv_obj_set_style_border_width(cont, 2, 0);
    lv_obj_set_style_border_opa(cont, LV_OPA_20, 0);
    lv_obj_set_style_border_color(cont, lv_palette_main(LV_PALETTE_GREY), 0);

    lv_obj_t *info = lv_label_create(cont);
    lv_obj_set_style_text_font(info, weather_info_font, 0);
    lv_obj_set_style_text_color(info, lv_color_black(), 0);
    lv_subject_add_observer_obj(&hmi_weather_subj_group, cb, info, NULL);

    lv_obj_t *title_ = lv_label_create(cont);
    lv_obj_set_style_text_font(title_, weather_info_title_font, 0);
    lv_obj_set_style_text_color(title_, lv_palette_main(LV_PALETTE_GREY), 0);
#if (LVGL_VERSION_MAJOR >= 9 && LVGL_VERSION_MINOR >= 4 && LVGL_VERSION_PATCH >= 0)
    lv_label_set_text(title_, lv_tr(title));
#else
    lv_label_set_text(title_, title);
#endif

    return cont;
}

static void temperature_ob_cb(lv_observer_t *observer, lv_subject_t *subject)
{
    lv_obj_t *label = lv_observer_get_target_obj(observer);
    lv_subject_t *high = lv_subject_get_group_element(subject, 5);
    lv_subject_t *low = lv_subject_get_group_element(subject, 6);

    int32_t temper_low = lv_subject_get_int(low);
    int32_t temper_high = lv_subject_get_int(high);
    lv_label_set_text_fmt(label, "%ld/%ld", temper_low, temper_high);

    lv_obj_t *parent = lv_observer_get_user_data(observer);
    if (temper_low < 15)
    {
        lv_obj_add_state(parent, LV_STATE_USER_1);
        lv_obj_remove_state(parent, LV_STATE_USER_2);
        lv_obj_remove_state(parent, LV_STATE_USER_3);
    }
    else if (temper_low >= 15 && temper_low < 30)
    {
        lv_obj_add_state(parent, LV_STATE_USER_2);
        lv_obj_remove_state(parent, LV_STATE_USER_1);
        lv_obj_remove_state(parent, LV_STATE_USER_3);
    }
    else
    {
        lv_obj_add_state(parent, LV_STATE_USER_3);
        lv_obj_remove_state(parent, LV_STATE_USER_1);
        lv_obj_remove_state(parent, LV_STATE_USER_2);
    }
}

static void weather_icon_ob_cb(lv_observer_t *observer, lv_subject_t *subject)
{
    lv_obj_t *obj = lv_observer_get_target_obj(observer);

    lv_subject_t *code_day_subj = lv_subject_get_group_element(subject, 2);
    lv_subject_t *code_night_subj = lv_subject_get_group_element(subject, 4);

    int32_t code_day = lv_subject_get_int(code_day_subj);
    int32_t code_night = lv_subject_get_int(code_night_subj);

    lv_subject_t *hour_subj = lv_subject_get_group_element(&hmi_dt_subj_group, 4);
    int32_t hour = lv_subject_get_int(hour_subj);

    char icon[32];

    if (hour >= 18 || hour < 6)
        lv_snprintf(icon, 32, "weather_%d", code_night);
    else if (hour >= 6 && hour < 18)
        lv_snprintf(icon, 32, "weather_%d", code_day);
    else
        lv_snprintf(icon, 32, "weather_99");

    lv_image_set_src(obj, hmi_png(icon));
}

static void weather_ob_cb(lv_observer_t *observer, lv_subject_t *subject)
{
    lv_obj_t *label = lv_observer_get_target_obj(observer);
    lv_subject_t *text_day_subj = lv_subject_get_group_element(subject, 1);
    lv_subject_t *text_night_subj = lv_subject_get_group_element(subject, 3);

    lv_subject_t *hour_subj = lv_subject_get_group_element(&hmi_dt_subj_group, 4);
    int32_t hour = lv_subject_get_int(hour_subj);

    if (hour >= 18 || hour < 6)
        lv_label_set_text(label, lv_subject_get_string(text_night_subj));
    else if (hour >= 6 && hour < 18)
        lv_label_set_text(label, lv_subject_get_string(text_day_subj));
}

static void city_ob_cb(lv_observer_t *observer, lv_subject_t *subject)
{
    lv_obj_t *label = lv_observer_get_target_obj(observer);
    lv_subject_t *subj = lv_subject_get_group_element(subject, 0);
    lv_label_set_text_fmt(label, "%s市", lv_subject_get_string(subj));
}

static void humidity_ob_cb(lv_observer_t *observer, lv_subject_t *subject)
{
    lv_obj_t *label = lv_observer_get_target_obj(observer);
    lv_subject_t *subj = lv_subject_get_group_element(subject, 9);
    lv_label_set_text_fmt(label, "%ld%%", lv_subject_get_int(subj));
}

static void precip_ob_cb(lv_observer_t *observer, lv_subject_t *subject)
{
    lv_obj_t *label = lv_observer_get_target_obj(observer);
    lv_subject_t *subj = lv_subject_get_group_element(subject, 7);
    lv_label_set_text_fmt(label, "%ld%%", lv_subject_get_int(subj));
}

static void wind_scale_ob_cb(lv_observer_t *observer, lv_subject_t *subject)
{
    lv_obj_t *label = lv_observer_get_target_obj(observer);
    lv_subject_t *subj = lv_subject_get_group_element(subject, 8);

#if (LVGL_VERSION_MAJOR >= 9 && LVGL_VERSION_MINOR >= 4 && LVGL_VERSION_PATCH >= 0)
    lv_label_set_text_fmt(label, "%d %s", lv_subject_get_int(subj), lv_tr("级"));
#else
    lv_label_set_text_fmt(label, "%ld级", lv_subject_get_int(subj));
#endif
}

static void function_box_create()
{
    lv_obj_t *function_box = lv_obj_create(home.root);
    lv_obj_set_flex_grow(function_box, 3);
    lv_obj_set_width(function_box, lv_pct(94));
    lv_obj_set_style_bg_opa(function_box, LV_OPA_TRANSP, 0);
    lv_obj_set_style_border_width(function_box, 0, 0);
    lv_obj_set_style_pad_all(function_box, 0, 0);
    lv_obj_set_style_clip_corner(function_box, true, 0);
    lv_obj_set_scroll_snap_x(function_box, LV_SCROLL_SNAP_CENTER);
    lv_obj_set_scrollbar_mode(function_box, LV_SCROLLBAR_MODE_OFF);
    lv_obj_set_flex_flow(function_box, LV_FLEX_FLOW_ROW);
    lv_obj_add_event_cb(function_box, function_box_scoll_ev_cb, LV_EVENT_SCROLL_END, NULL);

    page_indicator_create(home.root);

    lv_obj_t *page1 = function_box_page_create(function_box);
    lv_obj_t *page2 = function_box_page_create(function_box);

    lv_obj_t *led_strip_btn = function_btn_create(page1, "lightbulb", "灯带", hmi_pg_led_strip()->bg_color, led_strip_btn_click_cb);
    lv_obj_t *setting_btn = function_btn_create(page1, "gearshape", "设置", hmi_pg_setting()->bg_color, setting_btn_click_cb);
    lv_obj_t *setting_btn1 = function_btn_create(page1, "gearshape", "功能1", 0x3490de, NULL);
    lv_obj_t *setting_btn2 = function_btn_create(page2, "gearshape", "功能2", 0x3ec1d3, NULL);
    lv_obj_t *setting_btn3 = function_btn_create(page2, "gearshape", "功能3", 0x6639a6, NULL);

    lv_obj_set_grid_cell(led_strip_btn, LV_GRID_ALIGN_CENTER, 0, 1, LV_GRID_ALIGN_CENTER, 0, 1);
    lv_obj_set_grid_cell(setting_btn, LV_GRID_ALIGN_CENTER, 1, 1, LV_GRID_ALIGN_CENTER, 0, 1);
    lv_obj_set_grid_cell(setting_btn1, LV_GRID_ALIGN_CENTER, 2, 1, LV_GRID_ALIGN_CENTER, 0, 1);
    lv_obj_set_grid_cell(setting_btn2, LV_GRID_ALIGN_CENTER, 0, 1, LV_GRID_ALIGN_CENTER, 0, 1);
    lv_obj_set_grid_cell(setting_btn3, LV_GRID_ALIGN_CENTER, 1, 1, LV_GRID_ALIGN_CENTER, 0, 1);
}

static lv_obj_t *function_box_page_create(lv_obj_t *parent)
{
    lv_obj_t *page = lv_obj_create(parent);
    lv_obj_set_width(page, lv_pct(100));
    lv_obj_set_height(page, lv_pct(100));
    lv_obj_set_style_bg_opa(page, LV_OPA_TRANSP, 0);
    lv_obj_set_style_border_width(page, 0, 0);
    lv_obj_set_style_clip_corner(page, true, 0);
    lv_obj_add_flag(page, LV_OBJ_FLAG_SNAPPABLE);
    lv_obj_set_style_pad_hor(page, 0, 0);

    static int32_t col_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST};
    static int32_t row_dsc[] = {LV_GRID_CONTENT, LV_GRID_TEMPLATE_LAST};
    lv_obj_set_grid_dsc_array(page, col_dsc, row_dsc);

    page_indicator.max_index++;
    page_indicator_update();

    return page;
}

static lv_obj_t *function_btn_create(lv_obj_t *parent, const char *icon_path, const char *title, uint32_t bg_color, lv_event_cb_t ev_cb)
{
    lv_obj_t *btn = lv_obj_create(parent);
    lv_obj_set_width(btn, lv_pct(30));
    lv_obj_set_height(btn, lv_pct(100));
    lv_obj_set_style_radius(btn, 16, 0);
    lv_obj_set_style_border_width(btn, 0, 0);
    lv_obj_set_style_shadow_color(btn, lv_color_hex(bg_color), 0);
    lv_obj_set_style_shadow_width(btn, 16, 0);
    lv_obj_set_style_bg_color(btn, lv_color_hex(bg_color), 0);

    lv_obj_t *icon = lv_image_create(btn);
    lv_image_set_src(icon, hmi_png(icon_path));
    lv_obj_update_layout(btn);
    lv_obj_align(icon, LV_ALIGN_TOP_RIGHT, 0, 0);

    if (!function_btn_title_font)
        function_btn_title_font = hmi_tiny_ttf_create_static("def_font", 16, 0);
    lv_obj_t *title_ = lv_label_create(btn);
    lv_obj_set_width(title_, lv_pct(100));
    lv_obj_set_style_text_color(title_, lv_color_white(), 0);
    lv_obj_set_style_text_font(title_, function_btn_title_font, 0);
#if (LVGL_VERSION_MAJOR >= 9 && LVGL_VERSION_MINOR >= 4 && LVGL_VERSION_PATCH >= 0)
    lv_label_set_text(title_, lv_tr(title));
#else
    lv_label_set_text(title_, title);
#endif
    lv_label_set_long_mode(title_, LV_LABEL_LONG_MODE_SCROLL);
    lv_obj_align(title_, LV_ALIGN_BOTTOM_LEFT, 0, 0);

    lv_obj_update_layout(btn);
    function_btn_user_data_t *user_data = lv_malloc(sizeof(function_btn_user_data_t));
    user_data->target_width = lv_obj_get_width(btn);
    user_data->target_height = lv_obj_get_height(btn);

    lv_obj_add_event_cb(btn, function_btn_generic_ev_cb, LV_EVENT_ALL, user_data);
    if (ev_cb)
        lv_obj_add_event_cb(btn, ev_cb, LV_EVENT_CLICKED, NULL);

    return btn;
}

static void function_box_scoll_ev_cb(lv_event_t *e)
{
    lv_obj_t *function_box = lv_event_get_target_obj(e);

    lv_obj_update_layout(function_box);
    int32_t w = lv_obj_get_width(function_box);
    int32_t x = lv_obj_get_scroll_x(function_box);

    page_indicator.act_index = x / w;
    page_indicator_update();
}

static void function_btn_generic_ev_cb(lv_event_t *e)
{
    lv_event_code_t code = lv_event_get_code(e);
    lv_obj_t *obj = lv_event_get_current_target_obj(e);
    function_btn_user_data_t *user_data = lv_event_get_user_data(e);

    if (code == LV_EVENT_PRESSED)
    {
        lv_anim_t a;
        lv_anim_init(&a);
        lv_anim_set_duration(&a, 100);
        lv_anim_set_var(&a, obj);
        lv_anim_set_exec_cb(&a, (lv_anim_exec_xcb_t)lv_obj_set_width);
        lv_anim_set_values(&a, lv_obj_get_width(obj), user_data->target_width - 10);
        lv_anim_start(&a);

        lv_anim_set_exec_cb(&a, (lv_anim_exec_xcb_t)lv_obj_set_height);
        lv_anim_set_values(&a, lv_obj_get_height(obj), user_data->target_height - 10);
        lv_anim_start(&a);
    }
    else if (code == LV_EVENT_PRESS_LOST || code == LV_EVENT_RELEASED)
    {
        lv_anim_t a;
        lv_anim_init(&a);
        lv_anim_set_duration(&a, 100);
        lv_anim_set_var(&a, obj);
        lv_anim_set_exec_cb(&a, (lv_anim_exec_xcb_t)lv_obj_set_width);
        lv_anim_set_values(&a, lv_obj_get_width(obj), user_data->target_width);
        lv_anim_start(&a);

        lv_anim_set_exec_cb(&a, (lv_anim_exec_xcb_t)lv_obj_set_height);
        lv_anim_set_values(&a, lv_obj_get_height(obj), user_data->target_height);
        lv_anim_start(&a);
    }
    else if (code == LV_EVENT_DELETE)
    {
        if (user_data)
            lv_free(user_data);
    }
}

static void led_strip_btn_click_cb(lv_event_t *e)
{
    LV_UNUSED(e);
    hmi_pg_push(hmi_pg_led_strip());
}

static void setting_btn_click_cb(lv_event_t *e)
{
    LV_UNUSED(e);
    hmi_pg_push(hmi_pg_setting());
}

static void page_indicator_create()
{
    static bool first_run = true;
    if (first_run)
    {
        first_run = false;

        static lv_style_t def;
        lv_style_init(&def);
        lv_style_set_bg_opa(&def, LV_OPA_50);
        lv_style_set_bg_color(&def, lv_palette_main(LV_PALETTE_GREY));
        lv_style_set_size(&def, 6, 6);
        lv_style_set_radius(&def, LV_RADIUS_CIRCLE);

        static lv_style_t active;
        lv_style_init(&active);
        lv_style_set_bg_opa(&active, LV_OPA_COVER);
        lv_style_set_bg_color(&active, lv_color_white());
        lv_style_set_shadow_color(&active, lv_palette_main(LV_PALETTE_GREY));
        lv_style_set_shadow_width(&active, 6);
        lv_style_set_size(&active, 18, 6);
        lv_style_set_radius(&active, LV_RADIUS_CIRCLE);

        page_indicator.def = &def;
        page_indicator.active = &active;
    }

    page_indicator.max_index = 0;
    page_indicator.act_index = 0;

    page_indicator.obj = lv_obj_create(home.root);
    lv_obj_remove_style_all(page_indicator.obj);
    lv_obj_set_size(page_indicator.obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
    lv_obj_set_style_radius(page_indicator.obj, LV_RADIUS_CIRCLE, 0);
    lv_obj_set_style_pad_all(page_indicator.obj, 12, 0);
    lv_obj_set_style_pad_column(page_indicator.obj, 6, 0);
    lv_obj_set_flex_flow(page_indicator.obj, LV_FLEX_FLOW_ROW);

    lv_obj_t *indicator = lv_obj_create(page_indicator.obj);
    lv_obj_add_style(indicator, page_indicator.active, 0);
}

static void page_indicator_update()
{
    if (!page_indicator.obj)
        return;

    lv_obj_clean(page_indicator.obj);

    for (uint32_t i = 0; i < page_indicator.max_index; ++i)
    {
        lv_obj_t *indicator = lv_obj_create(page_indicator.obj);
        lv_obj_remove_style_all(indicator);
        if (i == page_indicator.act_index)
            lv_obj_add_style(indicator, page_indicator.active, 0);
        else
            lv_obj_add_style(indicator, page_indicator.def, 0);
    }
}
